#!/usr/bin/python
# -*- coding: utf-8 -*-

import sys
import os
import requests  # this is not default installed. 'pip install requests' helps you out.
import Utils
import json
import argparse

from lxml import html



class Common:

    ROOT_DIR = os.path.dirname(os.path.realpath(__file__))
    MEIZU_REDMINE_LOGIN_URL = "http://redmine.meizu.com/login?back_url=http://redmine.meizu.com/"
    MEIZU_REDMINE_LOGOUT_URL = "http://redmine.meizu.com/logout"
    REDMINE_HOME_URL = 'http://redmine.meizu.com'
    REDMINE_SEARCH_IDS_URL = 'http://redmine.meizu.com/issues?utf8=✓&set_filter=1&add_filter_select=&f[]=&f[]=bugids&op[bugids]==&v[bugids][]={:s}&c[]=project&c[]=tracker&c[]=status&c[]=priority&c[]=subject&c[]=author&c[]=assigned_to&c[]=updated_on&c[]=category&c[]=relations&c[]=tag_list&c[]=cf_84&group_by=&t[]='
    # tangqie's issues
    MY_ISSUE_URL ='http://redmine.meizu.com/issues?add_filter_select=&c[]=project&c[]=tracker&c[]=status&c[]=priority&c[]=subject&c[]=author&c[]=assigned_to&c[]=updated_on&c[]=relations&c[]=category&c[]=tag_list&f[]=&f[]=status_id&f[]=assigned_to_id&f[]=created_on&group_by=&op[assigned_to_id]==&op[created_on]=y&op[status_id]=o&per_page=100&set_filter=1&sort=priority:desc,id:desc&t[]=&utf8=✓&v[assigned_to_id][]=,169'

    dry = False
    verbose = 0
    open_issues = False

class Issue:

    config_data = None
    reg_cache = {}

    def __init__(self, id=0, link=None):
        self.id = id
        if not link:
            self.link = '/issues/' + str(int(id))
        self.relative_id = [] 
        self.status = ""
        self.priority = ""
        self.subject = ""
        self.author = ""
        self.assignee= ""
        self.orig_assignee= ""
        self.category = ""
        self.tags = ""
        self.notes = ""
        self.caseid = ""

        self.verbose = False

    def __str__(self):
        l = ["id: {:d}", "Title: {:s}", "链接: {:s}", "状态: {:s}", "优先级: {:s}", "作者: {:s}", "指派给: {:s}", "关联的问题:{:s}", "分类: {:s}", "标签: {:s}", "Case ID: {:s}"]
        return '\n'.join(l).format(self.id, self.subject.encode('utf8'), Common.REDMINE_HOME_URL + self.link, self.status.encode('utf8'), self.priority.encode('utf8'), self.author.encode('utf8'), self.orig_assignee.encode('utf8'), ",".join([str(x) for x in self.relative_id]), self.category.encode('utf8') if self.category else "", self.tags.encode('utf8'), self.caseid)

    def show(self):
        if Common.verbose > 0:
            Utils.LOG("======================")
            Utils.LOG(str(self))
            Utils.LOG("======================\n")
        else:
            if Common.verbose < 1:
                if self.status == u'已解决':
                    Utils.LOGN([str(self.id), " ", self.assignee, "[", self.caseid, "]", "[", self.status, "]", "[", self.tags, "]", " // ", self.subject])
                else:
                    Utils.LOG([str(self.id), " ", self.assignee, "[", self.caseid, "]", "[", self.status, "]", "[", self.tags, "]", " // ", self.subject])
            else:
                Utils.LOG(str(self))



class HTMLContent:
    def __init__(self):
        self.__init__(self, None)

    def __init__(self, content=None):
        self.parse(content)

    def parse(self, content):
        self.tree = html.fromstring(content) if content else None

    def get_elements(self, xpath):
        return self.tree.xpath(xpath)

    def get_issue_list(self):
        return self.get_elements('//*[@id="issues-list-table"]/tbody/tr')

class RedmineIssueHelper:

    def __init__(self):
        self.session = None
        self.verbose = 0
        self.dry = False

    # ATTENTION: login will return the same response whether the password is right or wrong
    def login(self, username=None, password=None):

        if not username:
            # get my username
            cmdline = 'sudo cat "/root/.meizu_email" | grep  --color -o -P "(?<=username=).*" '
            result = Utils.exec_cmd(cmdline, return_output = True)
            if result.hasError():
                Utils.LOGE("Get email username failed")
                sys.exit(-1)
            username = result.output[0]
        if not password:
            # get my password
            cmdline = 'sudo cat "/root/.meizu_email" | grep  --color -o -P "(?<=password=).*" '
            result = Utils.exec_cmd(cmdline, return_output = True)
            if result.hasError():
                Utils.LOGE("Get email password failed")
                sys.exit(-1)
            password = result.output[0]

        self.session = requests.session()
        payload = {'username':username,'password':password}
        res = self.session.post(Common.MEIZU_REDMINE_LOGIN_URL, data=payload)
        _show_verbose(3, Common.MEIZU_REDMINE_LOGIN_URL + " : " + username)

        return (res.ok, res.status_code)

    def logout(self):
        res = self.session.post(Common.MEIZU_REDMINE_LOGOUT_URL)
        return (res.ok, res.status_code)

    # return page content string 
    def get_page(self, url):
        res = self.session.get(url)

        # fix bug wrong decoding on linux. but on mac it's OK

        # -content: contains the raw data of web, which is utf8 here
        # -text: contains readable data of web, which is unicode here

        # when issue.subject = attr.text, the issue.subject is unicode-type anyway,
        # but its real data may be utf8 or unicode. 

        # issue.subject.encode('utf8') doesn't work
        # this function encode UNICODE data into specified-encoding, but the issue.subject contains in-fact non-UNICODE data.

        # so we use res.text instead of res.content, to make an unicode object contains real UNICODE data,
        # otherwise it won't display correctly

        #return res.content
        return res.text

    def get_issue_list(self, content):
        page = HTMLContent(content)
        elements = page.get_issue_list()
        issues = []
        for element in elements:
            issue = Issue()

            attr = element.xpath('./td[@class="id"]/a')[0]
            issue.id = int(attr.text)
            issue.link = attr.get("href")

            attr = element.xpath('./td[@class="status"]')[0]
            issue.status = attr.text

            attr = element.xpath('./td[@class="priority"]')[0]
            issue.priority= attr.text

            attr = element.xpath('./td[@class="cf_84 tag"]')[0]
            issue.caseid= attr.text

            attr = element.xpath('./td[@class="subject"]/a')[0]
            issue.subject = attr.text

            attr = element.xpath('./td[@class="author"]/a')
            if not attr:
                attr = element.xpath('./td[@class="author"]')
            issue.author = attr[0].text

            attr = element.xpath('./td[@class="assigned_to"]/a')[0]
            issue.orig_assignee = attr.text
            issue.assignee = attr.text

            attr = element.xpath('./td[@class="category"]')[0]
            issue.category = attr.text

            attr = element.xpath('./td[@class="tag_list"]')[0]
            if attr.text:
                issue.tags = attr.text

            attr = element.xpath('./td[@class="relations"]/span/a') 
            if attr:
                issue.relative_id = [int(x.text[1:]) for x in attr]

            issues.append(issue)
        return issues

def _show_verbose(v, string, color=None):
    if Common.verbose >= v:
        default_level = {0:None, 1:'I', 2:'T', 3:'N'}
        if not color and v in default_level:
            color = default_level[v]
        Utils.LOG(string, level=color)

def _get_ids_url(id_list=None):
    if id_list: # get issues from IDs
        ids = '+'.join([str(x) for x in id_list])
        URL = Common.REDMINE_SEARCH_IDS_URL.format(ids)
        _show_verbose(3, "get id list for " + ids + ": " + URL)
    else: # get all issues of tangqie
        URL = Common.MY_ISSUE_URL

    return URL


def _get_user_case_url(userid):
    return "http://redmine.meizu.com/issues?utf8=✓&set_filter=1&add_filter_select=&f[]=&f[]=status_id&op[status_id]=o&f[]=created_on&op[created_on]=y&f[]=cf_84&op[cf_84]=*&f[]=assigned_to_id&op[assigned_to_id]==&v[assigned_to_id][]=,{:d}&c[]=project&c[]=tracker&c[]=status&c[]=priority&c[]=subject&c[]=author&c[]=assigned_to&c[]=updated_on&c[]=category&c[]=tag_list&c[]=cf_84&group_by=&t[]=".format(userid)

def _get_issue_list(arg, func=_get_ids_url):

    URL = func(arg)
    if Common.dry:
        Utils.LOGN("get issues for " + URL)
        return None

    if __IssueHelper.session:
        html_content = __IssueHelper.get_page(URL)
        issues = __IssueHelper.get_issue_list(html_content)
        return issues
    else:
        return None


def _need_authen(args):
    return False if args.dry or (args.subcmd[0] == 'show_esservice_issue' and args.open_issues) else True

def change_tag_args(parser):
    parser.add_argument("tag", nargs=1, type=str)
    parser.add_argument("id_list", nargs='+', type=int)

def change_tag(args):
    tag = args.tag[0]
    id_list = args.id_list

    CONFIG_FILE = os.path.join(Common.ROOT_DIR, "config/redmine_config.json")
    with open(CONFIG_FILE) as f:
        config_data = json.load(f)
    status = config_data['status']
    if tag.rstrip('-') not in status:
        Utils.LOGE("Invalid tag: " + tag)
        return


    issues = _get_issue_list(id_list)
    if not issues:
        Utils.LOGE("No issue found")
        return

    for issue in issues:
        if Common.dry:
            Utils.LOGN(["update ", URL_TAG, " tag: ", self.tags])
        else:
            headers = {'content-type': 'application/json'}
            URL_TAG = Common.REDMINE_HOME_URL + "/issues/batch_tagging?ids[]=" + str(issue.id)
            session = __IssueHelper.session

            # add - suffix means delete
            payload_tag = {}
            if tag[-1] == '-':
                tag = tag[:-1]
                payload_tag['delete'] = 'true'
            else:   # delete previous status if existed
                if not tag == "AUTO_ASSIGNED":
                    for st in status:
                        if st in issue.tags:
                            if not st == "AUTO_ASSIGNED":
                                payload_tag['tag'] = st
                                payload_tag['delete'] = 'true'
                                session.put(URL_TAG, data=json.dumps(payload_tag), headers=headers)
                    payload_tag = {}

            payload_tag['tag'] = tag
            _show_verbose(2, ["update tag: ", tag])
            session.put(URL_TAG, data=json.dumps(payload_tag), headers=headers)

def show_esservice_issue_args(parser):
    parser.add_argument("--open",dest="open_issues", action="store_true", help="open in browser")
    parser.add_argument("id_list", nargs='*', type=int)

def show_esservice_issue(args):
    Common.open_issues = args.open_issues
    cmdline = 'sudo cat "/root/.meizu_email" | grep  --color -o -P "(?<=user=).*" '
    result = Utils.exec_cmd(cmdline, return_output = True)
    if result.hasError():
        Utils.LOGE("Get user failed")
        sys.exit(-1)
    user = result.output[0].decode('utf8')
    if Common.dry:
        Utils.LOGN(["show eservice issues for ", user])
        return


    CONFIG_FILE = os.path.join(Common.ROOT_DIR, "config/redmine_config.json")
    with open(CONFIG_FILE) as f:
        config_data = json.load(f)

    users = config_data['user']

    if user not in users:
        Utils.LOGE(["user ", user, " not recogonized"])
        sys.exit(-1)

    if Common.open_issues:
        URL = _get_user_case_url(users[user]) if not args.id_list else _get_ids_url(args.id_list)
        cmdline = 'open -a safari ' if Utils.isMac() else 'google-chrome '
        Utils.exec_cmd(cmdline + '"' + URL + '"')
    else:
        issues = filter(lambda x:x.caseid.startswith(u'ALPS'),_get_issue_list(args.id_list) if args.id_list else  _get_issue_list(users[user], _get_user_case_url))

        Utils.LOG("######################\n")
        for issue in issues:
                issue.show()
        Utils.LOG("######################\n")

if __name__ == "__main__":

    cmds = {'change_tag':change_tag, 
            'show_esservice_issue':show_esservice_issue
            }

    parser = argparse.ArgumentParser(description="web helper", add_help=False)
    Utils.installSubparser(parser, sys.modules[__name__], cmds)

    args = parser.parse_args()

    __IssueHelper = RedmineIssueHelper()
    Common.verbose = args.v
    Common.dry = args.dry

    need_authen = _need_authen(args)
    # login
    if need_authen:
        ret = __IssueHelper.login()
        _show_verbose(1, "login...")
        _show_verbose(3, "ATTENTION: response from login doesn't tell you whether success or fail.")

    try:
        args.func(args)
    finally:
        # logout
        if need_authen:
            ret = __IssueHelper.logout()
            _show_verbose(1, "logout...")



